//========================================================================
//  This software is free: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License Version 3,
//  as published by the Free Software Foundation.
//
//  This software is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License
//  Version 3 in the file COPYING that came with this distribution.
//  If not, see <http://www.gnu.org/licenses/>.
//========================================================================
/*!
\file    gui.h
\brief   GUI for Skeleton Pointing
\author  Joydeep Biswas, (C) 2011
*/
//========================================================================

#include <QWidget>
#include <QGLWidget>
#include <QMutex>
#include <QPainter>
#include <QPaintEvent>
#include <QtGui>
#include <GL/glu.h>
#include "geometry.h"
#include "timer.h"

#ifdef _WIN32
#include <gl/GL.h>
// MS Kinect SDK exists only for Windows
#include "MSR_NuiApi.h"
#include "MSR_NuiImageCamera.h"
#include "MSR_NuiProps.h"
#include "MSR_NuiSkeleton.h"
#include "NuiImageBuffer.h"
#else
// Emulate non-functional Kinect SDK on other platforms
#include "kinect_stubs.h"
#endif

#ifndef VECTOR_LIDAR_DISPLAY_H
#define VECTOR_LIDAR_DISPLAY_H

using namespace std;

#define GET_KINECT_COORDS(p) (p).z,(p).x,(p).y

class GLViewer : public QGLWidget
{
  Q_OBJECT
    
private:
  QMutex drawingMutex;
  
  //OpenGL primitives
  vector<line2d> glLines;
  vector<vector2d> glPoints;
  vector<vector3f> glLineColors;
  vector<vector3f> glPointColors;
  
  //Kinect Data
  NUI_SKELETON_FRAME skeletonFrame; /// detected skeletons
  double kinectHeight; /// Height of Kinect from the ground plane
  vector3f groundPlaneNormal;
  vector3d target3d;
  
  //Calibration Data
  vector3d calibrationPoints[4];
  vector<vector3d> dataPoints;
  vector<vector3d> dataVectors;

  double viewScale;
  double viewXOffset;
  double viewYOffset;
  double viewAngleZ;
  double viewAngleY;
  bool showCalibration;
  
  bool leftButton;
  bool midButton;
  bool rightButton;
  
  int mouseStartX;
  int mouseStartY;
  
protected:
  void paintEvent ( QPaintEvent * event );
  void wheelEvent ( QWheelEvent * event );
  void mouseMoveEvent ( QMouseEvent * event );
  void mousePressEvent( QMouseEvent * event );
  void mouseReleaseEvent( QMouseEvent * event );
  void resizeEvent ( QResizeEvent * event );
  void initializeGL();
  void resizeGL(int width, int height);
  QSize sizeHint () const {return QSize(800,600);} 

public slots:
  void chkShowCalibrationChanged ( int state );
  void resetView();
private slots:
  void redraw(){ this->update(); }
signals:
  void postRedraw();

public: 
  GLViewer(QWidget *parent = 0);
  void drawCalibrationSetup();
  void drawCalibrationData();
  void drawGroundPlane();
  void drawKinectViewFrustrum();
  void drawDisplayPlane();
  void drawInstructions(QPainter* painter);
  void drawSkeletons(NUI_SKELETON_FRAME skeletons);
  template <class vec> void drawGlLine(vec p1, vec p2);
  void updateData(NUI_SKELETON_FRAME _skeletonFrame);
  void updateDrawingPrimitives(vector<line2d> &_glLines, vector<vector2d> &_glPoints, vector<vector3f> &_glLineColors, vector<vector3f> &_glPointColors);
  /// Updates the GUI with the calibration status
  void calibrationStatusUpdate(vector3d _calibrationPoints[4], vector<vector3d>& _dataPoints, vector<vector3d>& _dataVectors);
  void updateTarget(vector3d target3d);
};

class SkeletonPointingGUI : public QWidget
{
  Q_OBJECT
  
private:
  /// Callback to be called when the user clicks on the "calibrate" button
  void (*startCalibrationCallback)(int);
  /// Callback to be called when the user clicks on the "stop calibrate" button, thus aborting calibration
  void (*stopCalibrationCallback)(void);
  /// Callback to be called when the user clicks on the load / save buttons (parameter = true for load, false for save)
  void (*calibrationLoadSave)(bool);
  void keyPressEvent ( QKeyEvent * event );
  QMutex guiMutex;
  bool calibrating;
  
  //UI elements
  QGroupBox* calibrationBox;
  QPushButton* btnCalibrate;
  QPushButton* btnSave;
  QPushButton* btnLoad;
  QRadioButton* optPoint[3];
  QCheckBox* chkShowCalibration;
  QGroupBox* controlPanel;
  QHBoxLayout* mainLayout;
  GLViewer* glViewer;
  
private slots:
  void btnCalibrateClicked(bool checked=false);
  void btnCalibrationLoadSave(bool checked=false);
  
public: 
  SkeletonPointingGUI(QWidget *parent = 0);
  void createGUI();
  void updateData(NUI_SKELETON_FRAME _skeletonFrame){if(glViewer) glViewer->updateData(_skeletonFrame);}
  void setCallbacks(void (*_startCalibrationCallback)(int), void (*_stopCalibrationCallback)(), void (*_loadSaveCallback)(bool));
  /// Returns true if the calibration procedure has been started (and not stopped)
  bool isCalibrating();
  /// Updates the GUI with the calibration status
  void calibrationStatusUpdate(vector3d calibrationPoints[4], vector<vector3d>& _dataPoints, vector<vector3d>& _dataVectors);
  /// Update Pointing Target
  void updateTarget(vector3d target3d){if(glViewer) glViewer->updateTarget(target3d);}
};

#endif //VECTOR_LIDAR_DISPLAY_H
